package com.hitechbangla.fctu.utility;

import com.hitechbangla.fctu.base.GameScene;
import com.hitechbangla.fctu.base.Location;
import com.hitechbangla.fctu.base.aircraft.Aircraft;
import com.hitechbangla.fctu.base.radar.Radar;
import com.hitechbangla.fctu.utility.Const.*;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.List;
import java.util.Random;
/**
 *
 * @author deb kanti
 */
public class GeneralUtil 
{
    private static final float angleOneQuadrant = 90f;
    private static final float angleThreeQuadrant = 270f;
    private static final float angleFourQuadrant = 360f;    
    private static final int range = 5; //range of clicked position
    
    public static float square(float x)
    {
        return x*x;
    }
    
    public static float degreeToRadian(float angleInDegree)
    {
        float angleInRadian = (float) (Math.PI * angleInDegree / 180);
        return angleInRadian;
    }
    
   public static float radianToDegree(float angleInRadian)
    {
        float angleInDegree = (float) (180 * angleInRadian / Math.PI);
        return angleInDegree;
    }
    
    public static ObjectInputStream getObjectInputStreamFromFileStream(String fileName)
    {
        try
        {
            FileInputStream fileInputStream = new FileInputStream(fileName);
            ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream);
            return objectInputStream;
        }
        catch(Exception ex)
        {
            return null;
        }
    }
    
    public static ObjectOutputStream getObjectOutputStreamFromFileStream(String fileName)
    {
        try
        {
            FileOutputStream fileOutputStream = new FileOutputStream(fileName);
            ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream);
            return objectOutputStream;
        }
        catch(Exception ex)
        {
            return null;
        }
    }
    
    
    public static boolean isLyingInCone(Location target, Location apex, Location base, double aperture){
	aperture = GeneralUtil.degreeToRadian((float)aperture);
	double halfAperture = aperture/2.f;
	
	// Vector pointing to target from apex
	Location apexToTargetVect = dif(apex, target);
	
	// Vector pointing from apex to circle-center point.
	Location axisVect = dif(apex, base);
	
	// target is lying in cone only if it's lying in infinite version of its cone
	// that is, not limited by "round basement".
	// We'll use dotProd() to determine angle between apexToTargetVect and axis.
	boolean isInInfiniteCone = dotProd(apexToTargetVect, axisVect)
		/ magn(apexToTargetVect) / magn(axisVect)
		>
		//We can safely compare cos() of angle between vectors instead of bare angles.
		Math.cos(halfAperture);
	/*double dotP = dotProd(apexToTargetVect, axisVect) / (magn(apexToTargetVect) * magn(axisVect));
	if (dotP > Math.cos(halfAperture)){
	    isInInfiniteCone = true;
	}*/
	if (!isInInfiniteCone)
	    return false;
	
	return true;
	
	// target is contained in cone only if projection of apexToTargetVect to axis is shorter than axis.
	// We'll use dotProd() to figure projection length.
	/*boolean isUnderRoundCap = dotProd(apexToTargetVect, axisVect)
				  /magn(axisVect)
				  <
				  magn(axisVect);
	
	return isUnderRoundCap; */
    }	// End of isLyingInCone function
    
    
    
    public static double dotProd(Location a, Location b){
	return a.getX()*b.getX() + a.getY()*b.getY();
    }	// End of dotProd function
    
    
    public static Location dif(Location a, Location b){
	Location retValue = new Location((a.getX() - b.getX()), (a.getY() - b.getY()), 0 );
	
	return retValue;  
    }	// End of dif fucntion
    
    
    public static double magn(Location a){
	return (Math.sqrt(a.getX()*a.getX() + a.getY()*a.getY()));
    }
    
    
    public static double distance(double x1, double y1, double z1, double x2, double y2, double z2){
	return (Math.sqrt((x1-x2)*(x1-x2) + (y1-y2)*(y1-y2) + (z1-z2)*(z1-z2)));
    }
    
    public static double distance2D(double x1, double y1,double x2, double y2){
	return (Math.sqrt((x1-x2)*(x1-x2) + (y1-y2)*(y1-y2)));
    }
    
    
    public static float getVectorX(float scalar, float heading)
    {
	return scalar*(float)Math.sin(degreeToRadian(heading));	
    }
    
    public static float getVectorY(float scalar, float heading)
    {
	return scalar*(float)Math.cos(degreeToRadian(heading));	
    }
    
    
    public static double getHeadingWithOther(double x1, double y1, double x2, double y2)
    {
	double intervalx = x2 - x1;
	double intervaly = y2 - y1;
	
	double angleInRadian = (double)Math.atan(intervaly/intervalx);
	
	angleInRadian = Math.abs(angleInRadian);
	double angleInDegree = Math.abs((double)(180*angleInRadian/Math.PI));
	
	

	if (intervalx>=0 && intervaly>=0) // first quadrant
	{
		return angleOneQuadrant-angleInDegree;
	}
	
	else if(intervalx>=0 && intervaly<=0) //second quadrant
	{
		return angleOneQuadrant + angleInDegree;
	}
	
	else if(intervalx<=0 && intervaly<=0) // third quadrant
	{
		return angleThreeQuadrant - angleInDegree;
	}
	
	else // fourth quadrant
	{
		return angleThreeQuadrant + angleInDegree;
	}
	
    }
    
    
    public static boolean isPointCloseToLine(double pointX, double pointY, double line1X, double line1Y, double line2X, double line2Y)
    {
	/*
	float distanceToPoint = (float)distance2D(pointX, pointY , 0.0f, 0.0f);
	float distanceToLine1 = (float)distance2D(line1X, line1Y , 0.0f, 0.0f);
	float distanceToLine2 = (float)distance2D(line2X, line2Y , 0.0f, 0.0f);
	
	if(isInBetween(distanceToPoint, distanceToLine1, distanceToLine2))
	{
	    float angle1Radian = (float)Math.atan(pointY/pointX);
	    float angle2Radian = (float)Math.atan((line2Y-line1Y)/(line2X-line1X));
	    
	    float angle1Degree = radianToDegree(angle1Radian);
	    float angle2Degree = radianToDegree(angle2Radian);
	    
	    if(isAngleClose(angle1Degree,angle2Degree))
	    {
		return true;
	    }
	    else
	    {
		return false;
	    }
	}	
	else
	{
	    return false;
	}  
   	*/
	
	float dist1 = (float)distance2D(pointX, pointY, line1X, line1Y);
	float dist2 = (float)distance2D(pointX, pointY, line2X, line2Y);
	
	float distanceWithPoint = dist1 + dist2;
	float distanceWithoutPoint = (float)distance2D(line1X, line1Y, line2X, line2Y);
	
	return close(distanceWithPoint, distanceWithoutPoint);
	
    }
    
    private static boolean close(float p, float p1)
    {
	return ((Math.abs(p-p1))/p1<0.1);	
    }
    
    public static void UpdateGameScene(GameScene gameScene) {
        if(gameScene.HTBParent.isNetworkConnected) {
                if(gameScene.HTBParent.isGameStarter) {
                    gameScene.HTBParent.fctuClient.SendGameScene();
                }
                else {
                    gameScene.HTBParent.getClientConnection.SendGameScene();
                }
                    
            }
    }
    
    public static void UpdateCommand(GameScene gameScene, String command) {
        if(gameScene.HTBParent.isNetworkConnected) {
                if(gameScene.HTBParent.isGameStarter) {
                    gameScene.HTBParent.fctuClient.SendGameCommand(command);
                }
                else {
                    gameScene.HTBParent.getClientConnection.SendGameCommand(command);
                }
                    
            }
    }
    
    public static void StopGame(GameScene gameScene) {
        if(gameScene.HTBParent.isNetworkConnected) {
                if(gameScene.HTBParent.isGameStarter) {
                    gameScene.HTBParent.fctuClient.SendGameStop();
                }
                else {
                    gameScene.HTBParent.getClientConnection.SendGameStop();
                }
                    
            }
    }
    
    public static void StartGame(GameScene gameScene) {
        if(gameScene.HTBParent.isNetworkConnected) {
                if(gameScene.HTBParent.isGameStarter) {
                    gameScene.HTBParent.fctuClient.SendGameStart();
                }
                else {
                    gameScene.HTBParent.getClientConnection.SendGameStart();
                }
                    
            }
    }
    
    
    public static void executeCommand(GameScene gameScene, String command) {
                String callSignCommand = command.substring(0, 3);
                
                for (Aircraft aircraft : gameScene.airCraftList) {
                    if (aircraft.getAircraftState().getCallSign().equals(callSignCommand)) {

                        aircraft.executeCommand(command);
                        
                    }
                }
    }
    
    
    /*
    private static boolean  isInBetween(float distance, float distance1, float distance2)
    {
	if(distance1 < distance2)
	{
	    if(distance > distance1 & distance < distance2)
	    {
		return true;
	    }
	    else
	    {
		return false;
	    }
		   
	}
	else
	{
	    if(distance > distance2 & distance < distance1)
	    {
		return true;
	    }
	    else
	    {
		return false;
	    }
	}
    }
    */
    // for testing purpose
    public static void main(String a[])
    {
	boolean result = isPointCloseToLine(98,69, 59,74, 169, 60);
	//System.out.println("result:" + result);
    }

    /* this will be removed in future
    private static boolean isAngleClose(float angle1Degree, float angle2Degree) 
    {
	float angleFactor = (float)Math.abs(angle1Degree-angle2Degree)/angle1Degree;
	return (Math.abs(angleFactor) <0.1);	
    }
    */
    
    /*
     * x and y must be converted to nautical mile system before calling this function 
     * from caller
     * 
     */
    public static Aircraft getAircraftClicked(List<Aircraft> airCraftList, double x, double y){
	
	for (Aircraft aircraft : airCraftList) {
	    //Detected detected = (Detected) object;
	    //Aircraft aircraft = detected.getAircraft();

	    ////System.out.println("from autolock: aircraft detected");
	    //Point point = new Point
	    if (withinRange(aircraft, x, y)) {
		return aircraft;
	    }
	}
	return null;
    }

    private static boolean withinRange(Aircraft aircraft, double x, double y) {
	//Point point2 = new Point();
	//point2.setLocation(point.getX() * HTB_MAIN.one_nm_equivalent_pixel, point.getY() * HTB_MAIN.one_nm_equivalent_pixel);
	double distX = Math.abs((double) x - aircraft.getAircraftState().getLocation().getX());
	double distY = Math.abs((double) (y - aircraft.getAircraftState().getLocation().getY()));
	return (distX < range && distY < range);
    }
    
    public static Radar getRadarClicked(List<Radar> radarList, double x, double y){
	
	for (Radar radar : radarList) {
	    //Detected detected = (Detected) object;
	    //Aircraft aircraft = detected.getAircraft();

	    ////System.out.println("from autolock: aircraft detected");
	    //Point point = new Point
	    if (radarWithinRange(radar, x, y)) {
		return radar;
	    }
	}
	return null;
    }
    
    private static boolean radarWithinRange(Radar radar, double x, double y) {
	//Point point2 = new Point();
	//point2.setLocation(point.getX() * HTB_MAIN.one_nm_equivalent_pixel, point.getY() * HTB_MAIN.one_nm_equivalent_pixel);
//	float distX = Math.abs((float) x - aircraft.getAircraftState().getLocation().getX());
//	float distY = Math.abs((float) (y - aircraft.getAircraftState().getLocation().getY()));
	double distX = Math.abs((double) x - radar.getX() );
	double distY = Math.abs((double) y - radar.getY() );
	return (distX < range && distY < range);
    }
    
    
     public static int getRandomIntegerInRange(int start, int end, Random random){
	if ( start > end ) {
	  throw new IllegalArgumentException("Start cannot exceed End.");
	}
	//get the range, casting to long to avoid overflow problems
	long range = (long)end - (long)start + 1;
	// compute a fraction of the range, 0 <= frac < range
	long fraction = (long)(range * random.nextDouble());
	int randomNumber =  (int)(fraction + start);
	return randomNumber;
    }
    
    public static void log(String aMessage) {
	    //System.out.println(aMessage);
    }
    
}

